package com.christina.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * 
 * @author Christina
 */
@Slf4j
@Component
public class RedisUtil {

    private final RedisTemplate<String, Object> redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;

    public RedisUtil(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    // ========== 通用操作 ==========

    /**
     * 设置过期时间
     */
    public boolean expire(String key, long time) {
        return expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 设置过期时间
     */
    public boolean expire(String key, long time, TimeUnit timeUnit) {
        try {
            if (time > 0 && key != null) {
                return Boolean.TRUE.equals(redisTemplate.expire(key, time, timeUnit));
            }
            return false;
        } catch (Exception e) {
            log.error("设置过期时间失败，key={}, time={}, timeUnit={}", key, time, timeUnit, e);
            return false;
        }
    }

    /**
     * 获取过期时间
     */
    public long getExpire(String key) {
        return getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 获取过期时间
     */
    public long getExpire(String key, TimeUnit timeUnit) {
        try {
            Long expire = redisTemplate.getExpire(key, timeUnit);
            return expire != null ? expire : -1;
        } catch (Exception e) {
            log.error("获取过期时间失败，key={}, timeUnit={}", key, timeUnit, e);
            return -1;
        }
    }

    /**
     * 判断key是否存在
     */
    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("判断key是否存在失败，key={}", key, e);
            return false;
        }
    }

    /**
     * 删除缓存
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    // ========== String操作 ==========

    /**
     * 普通缓存获取
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error("设置缓存失败，key={}, value={}", key, value, e);
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     */
    public boolean set(String key, Object value, long time) {
        return set(key, value, time, TimeUnit.SECONDS);
    }

    /**
     * 普通缓存放入并设置时间
     */
    public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, timeUnit);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("设置缓存失败，key={}, value={}, time={}, timeUnit={}", key, value, time, timeUnit, e);
            return false;
        }
    }

    /**
     * 递增
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        try {
            Long result = redisTemplate.opsForValue().increment(key, delta);
            return result != null ? result : -1;
        } catch (Exception e) {
            log.error("递增操作失败，key={}, delta={}", key, delta, e);
            return -1;
        }
    }

    /**
     * 递减
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        try {
            Long result = redisTemplate.opsForValue().increment(key, -delta);
            return result != null ? result : -1;
        } catch (Exception e) {
            log.error("递减操作失败，key={}, delta={}", key, delta, e);
            return -1;
        }
    }

    /**
     * 原子递增并设置过期时间
     */
    public long incrWithExpire(String key, long delta, long expireTime, TimeUnit timeUnit) {
        try {
            Long value = redisTemplate.opsForValue().increment(key, delta);
            if (value != null && value == delta) {
                // 第一次设置，需要设置过期时间
                redisTemplate.expire(key, expireTime, timeUnit);
            }
            return value != null ? value : -1;
        } catch (Exception e) {
            log.error("原子递增并设置过期时间失败，key={}, delta={}, expireTime={}, timeUnit={}", key, delta, expireTime, timeUnit, e);
            return -1;
        }
    }

    // ========== 字符串操作 ==========

    /**
     * 获取字符串值
     */
    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 设置字符串值
     */
    public void setString(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置字符串值并设置过期时间
     */
    public void setString(String key, String value, long timeout, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    // ========== Hash操作 ==========

    /**
     * Hash操作：获取Hash中的字段值
     */
    public Object hGet(String key, String field) {
        try {
            return redisTemplate.opsForHash().get(key, field);
        } catch (Exception e) {
            log.error("Hash获取字段值失败，key={}, field={}", key, field, e);
            return null;
        }
    }

    /**
     * Hash操作：设置Hash中的字段值
     */
    public boolean hSet(String key, String field, Object value) {
        try {
            redisTemplate.opsForHash().put(key, field, value);
            return true;
        } catch (Exception e) {
            log.error("Hash设置字段值失败，key={}, field={}, value={}", key, field, value, e);
            return false;
        }
    }

    /**
     * Hash操作：获取Hash中的所有字段和值
     */
    public Map<Object, Object> hGetAll(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("Hash获取所有字段值失败，key={}", key, e);
            return new HashMap<>();
        }
    }

    /**
     * Hash操作：设置Hash中的多个字段值
     */
    public boolean hSetAll(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error("Hash设置多个字段值失败，key={}, map={}", key, map, e);
            return false;
        }
    }

    /**
     * Hash操作：删除Hash中的字段
     */
    public void hDel(String key, Object... fields) {
        try {
            redisTemplate.opsForHash().delete(key, fields);
        } catch (Exception e) {
            log.error("Hash删除字段失败，key={}, fields={}", key, fields, e);
        }
    }

    // ========== List操作 ==========

    /**
     * List操作：获取列表指定范围的值
     */
    public List<Object> lRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            log.error("List获取范围值失败，key={}, start={}, end={}", key, start, end, e);
            return new ArrayList<>();
        }
    }

    /**
     * List操作：在列表头部添加元素
     */
    public boolean lLeftPush(String key, Object value) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("List左侧添加元素失败，key={}, value={}", key, value, e);
            return false;
        }
    }

    /**
     * List操作：在列表尾部添加元素
     */
    public boolean lRightPush(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("List右侧添加元素失败，key={}, value={}", key, value, e);
            return false;
        }
    }

    /**
     * List操作：获取列表长度
     */
    public long lSize(String key) {
        try {
            Long size = redisTemplate.opsForList().size(key);
            return size != null ? size : 0;
        } catch (Exception e) {
            log.error("List获取长度失败，key={}", key, e);
            return 0;
        }
    }

    // ========== Set操作 ==========

    /**
     * Set操作：向集合添加元素
     */
    public boolean sAdd(String key, Object... values) {
        try {
            redisTemplate.opsForSet().add(key, values);
            return true;
        } catch (Exception e) {
            log.error("Set添加元素失败，key={}, values={}", key, values, e);
            return false;
        }
    }

    /**
     * Set操作：获取集合所有元素
     */
    public Set<Object> sMembers(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error("Set获取所有元素失败，key={}", key, e);
            return new HashSet<>();
        }
    }

    /**
     * Set操作：判断元素是否在集合中
     */
    public boolean sIsMember(String key, Object value) {
        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            log.error("Set判断元素是否存在失败，key={}, value={}", key, value, e);
            return false;
        }
    }

    // ========== 批量操作 ==========

    /**
     * 批量获取
     */
    public List<Object> multiGet(Collection<String> keys) {
        try {
            return redisTemplate.opsForValue().multiGet(keys);
        } catch (Exception e) {
            log.error("批量获取失败，keys={}", keys, e);
            return new ArrayList<>();
        }
    }

    /**
     * 批量设置
     */
    public boolean multiSet(Map<String, Object> map) {
        try {
            redisTemplate.opsForValue().multiSet(map);
            return true;
        } catch (Exception e) {
            log.error("批量设置失败，map={}", map, e);
            return false;
        }
    }

    // ========== 分布式锁 ==========

    /**
     * 获取分布式锁
     */
    public boolean lock(String key, String value, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.execute(
                (RedisCallback<Boolean>) connection -> {
                    RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                    return connection.set(
                        serializer.serialize(key), 
                        serializer.serialize(value), 
                        Expiration.from(expireTime, timeUnit), 
                        SetOption.SET_IF_ABSENT
                    );
                }
            );
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.error("获取分布式锁失败，key={}, value={}, expireTime={}, timeUnit={}", key, value, expireTime, timeUnit, e);
            return false;
        }
    }

    /**
     * 释放分布式锁
     */
    public boolean unlock(String key, String value) {
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Long result = redisTemplate.execute(
                (RedisCallback<Long>) connection -> {
                    RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                    Object obj = connection.eval(
                        script.getBytes(), 
                        org.springframework.data.redis.connection.ReturnType.INTEGER, 
                        1, 
                        serializer.serialize(key), 
                        serializer.serialize(value)
                    );
                    return (Long) obj;
                }
            );
            return result != null && result > 0;
        } catch (Exception e) {
            log.error("释放分布式锁失败，key={}, value={}", key, value, e);
            return false;
        }
    }

    // ========== 缓存键生成工具 ==========

    /**
     * 生成用户缓存键
     */
    public static String getUserCacheKey(Long userId) {
        return "user:" + userId;
    }

    /**
     * 生成聊天历史缓存键
     */
    public static String getChatHistoryCacheKey(Long userId) {
        return "chat:history:" + userId;
    }

    /**
     * 生成JWT黑名单缓存键
     */
    public static String getJwtBlacklistKey(String token) {
        return "jwt:blacklist:" + token;
    }

    /**
     * 生成验证码缓存键
     */
    public static String getVerificationCodeKey(String phone) {
        return "verification:code:" + phone;
    }

    /**
     * 生成限流缓存键
     */
    public static String getRateLimitKey(String identifier, String action) {
        return "rate:limit:" + identifier + ":" + action;
    }

    /**
     * 生成分布式锁键
     */
    public static String getLockKey(String resource) {
        return "lock:" + resource;
    }

    /**
     * 生成计数器键
     */
    public static String getCounterKey(String name) {
        return "counter:" + name;
    }
}